`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/11/26 17:02:49
// Design Name: 
// Module Name: rv_dmem
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
`include "msg_config.v"

//2KB/bank *16 banks =32KB
module rv_dmem#(parameter addr_width = 11)(
    input CLK,input RST,
    //Supervior --> DMEM
    input EBUS_SEL,

    //EX --> DMEM
    input DMEM_CS,
    input [31:0] ADDR,
    input [4:0] INST_TYPE,
    input [2:0] FUNC3,
    input [31:0] DIN,

    //EBUS --> DMEM
    input CS_EBUS,
    input WR_EBUS,
    input [`EBUS_ADDR_WIDTH-1:0] ADDR_EBUS,
    input [127:0] DIN_EBUS,

    output reg [31:0] DOUT
    );
    wire [3:0] addr4;
    assign addr4=ADDR[3:0];

    wire [15:0] rv_cs;
    wire rv_wr;
    wire [127:0] rv_din;
    wire [127:0] rv_dout;
    wire [16*addr_width-1:0] rv_addr;

    reg [3:0] word_cs;
    always @(*) begin
        case(FUNC3[1:0])
        2'b00: word_cs=4'b0001&{4{DMEM_CS}};
        2'b01: word_cs=4'b0011&{4{DMEM_CS}};
        2'b10: word_cs=4'b1111&{4{DMEM_CS}};
        default:word_cs=4'b0;
        endcase
    end
    barrier_rol_shift16 CS_ROL16({12'b0,word_cs},addr4,rv_cs);
    
    assign rv_wr=((DMEM_CS==1'b1)&(INST_TYPE==`INST_STORE))?1'b1:1'b0;
    
    rol_shift16B ROL_SHIFT16B({4{DIN}},addr4,rv_din);
    
    //Addr
    wire [1:0] len1;
    assign len1=(FUNC3[1:0]==2'b10)?2'b11:FUNC3[1:0];
    wire addr_carry;
    wire [3:0] unused;
    adderx2w #(.WIDTH(4)) ADDR_CARRY(ADDR[3:0],{2'b0,len1},unused,addr_carry);
    wire [addr_width-1:0] addr1;
    assign addr1=ADDR[addr_width+4-1:4]+addr_carry;
    assign rv_addr[16*addr_width-1:3*addr_width]={13{ADDR[addr_width+4-1:4]}};
    assign rv_addr[3*addr_width-1:0]={3{addr1}};
    

    wire [15:0] bank_cs;
    wire bank_wr;
    wire [127:0] dmem_din;
    wire [127:0] dmem_dout;
    wire [16*addr_width-1:0] bank_addr;

    assign bank_cs=(EBUS_SEL==1'b1)?{16{CS_EBUS}}:rv_cs;
    assign bank_wr=(EBUS_SEL==1'b1)?WR_EBUS:rv_wr;
    assign dmem_din=(EBUS_SEL==1'b1)?DIN_EBUS:rv_din;
    assign bank_addr=(EBUS_SEL==1'b1)?{16{ADDR_EBUS}}:rv_addr;
    genvar i;
    generate
        for(i=0;i<16;i=i+1) begin
            sram #(.addr_width(addr_width),.data_width(8)) WMEM_BANK(
                CLK,bank_cs[i],bank_wr,bank_addr[i*addr_width+addr_width-1:i*addr_width],
                dmem_din[i*8+7:i*8],dmem_dout[i*8+7:i*8]);
        end
    endgenerate
    wire [3:0] addr4_reg;
    regw #(.WIDTH(4)) ADDR4_REG(CLK,RST,1'b1,addr4,addr4_reg);
    ror_shift16B DOUT_ROR(dmem_dout,addr4_reg,rv_dout);

    always @(*) begin
        case(FUNC3)
            3'b000: DOUT={{24{rv_dout[7]}},rv_dout[7:0]};
            3'b001: DOUT={{16{rv_dout[15]}},rv_dout[15:0]};
            3'b010: DOUT=rv_dout[31:0];
            3'b100: DOUT={24'b0,rv_dout[7:0]}; //unsigned
            3'b101: DOUT={16'b0,rv_dout[15:0]}; //unsigned
            default: DOUT=32'b0;
        endcase
    end
    
endmodule
